//
//  SPEntry.m
//  Sophie
//
//  Created by Anton Chebotaev on 4/10/09.
//  Copyright 2009 IFMO. All rights reserved.
//

#import "SPEntryDocument.h"


@interface SPEntryController (Private)
- (void)updateView;
- (void)updateModel;

- (void)regenerateToolbarMenus;
- (void)regenerateUserpicsMenu;
- (void)subscribeCallbacks;
- (NSString *)HTMLContent;
@end

@interface SPEntryController (Callbacks)
- (void)accountWillReloadCallback;
- (void)accountDidReloadCallback;

- (void)accountWillReloadUserpicsCallback;
- (void)accountDidReloadUserpicsCallback;

- (void)entryWillPost;
- (void)entryDidPost;
- (void)entryFailedPost;

- (void)securityMenuWillOpen;
- (void)journalsMenuWillOpen;
@end

#pragma mark -
@implementation SPEntryController

@synthesize journalsMenu;
@synthesize userpicsMenu;
@synthesize securityMenu;
@synthesize entry;

- (id)init
{
    self = [super init];
    if (self) {
		entry = [[LJEntry alloc] init];
		
		[entry setPrivacyLevel:[[SPPreferencesController sharedController] defaultPrivacyLevel]];
		if ([[SPPreferencesController sharedController] shouldDetectMusicForNewEntry]) {
			[entry setCurrentMusic:[SPAppDelegate getCurrentMusicString]];
		}
		
		// if default journal exists, set it
		LJAccount *account = [[SPAccountController sharedController] sharedAccount];
		if (account) {
			LJJournal *journal = [account defaultJournal];

			[entry setJournal:journal];
		}			
		
		// set userpic
		[entry setUserpic:[account defaultUserpic]];		
		
		// not related to entry stuff
		previousView = nil;
		previewWebView = [[WebView alloc] initWithFrame:[[entryWindow contentView] bounds]
											  frameName:@""
											  groupName:@""];
	}
    return self;
}

- (void)dealloc {
	[entry release];
	[super dealloc];
}

- (NSString *)windowNibName
{
	return @"SophieEntry";
}

- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
    [super windowControllerDidLoadNib:aController];
	
	// Set default enabled status
	LJAccount* account = [[SPAccountController sharedController] sharedAccount];
	if (account) {
		[journalButton setEnabled:YES];
		[securityButton setEnabled:YES];
	}
	
	// Regenerate interface items
	[self regenerateToolbarMenus];
	[self regenerateUserpicsMenu];
		
	// Set custom options
	[drawer setPreferredEdge:NSMinYEdge];
	drawerOpened = NO;
	
	[userpicsButton setImagePosition:NSNoImage];
	[tagsManager setEntry:entry];
	
	// Consolidate with model
	[self updateView];
		
	// After all UI is loaded and prepared we can sunscribe to notofocations
	[self subscribeCallbacks];
}

- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
{
	// End editing
    [[self windowForSheet] endEditingFor:nil];
	
    // Create an NSData object from the employees array
    return [NSKeyedArchiver archivedDataWithRootObject:entry];	
}

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError
{
	LJEntry *newEntry = nil;
	@try {
		newEntry = [NSKeyedUnarchiver unarchiveObjectWithData:data];
		BOOL successfullyAssigned = NO;
		
		for(LJJournal *journal in [[[SPAccountController sharedController] sharedAccount] journals]){
			//NOTE: Review why without additional variables comparator fails
			int journalHash = [journal hash];
			if ([newEntry journalHash] == journalHash) {
				[newEntry setJournal:journal];
				successfullyAssigned = YES;
				break;
			}
		}
		
		if (!successfullyAssigned) {
			NSLog(@"ERROR: Unable to unarchive entry. Journal is missing.");
			return NO;
		}
	}
	@catch (NSException *e) {
		if (outError) {
			NSDictionary *info = [NSDictionary dictionaryWithObject:@"Unable to read data."
															 forKey:NSLocalizedFailureReasonErrorKey];
			*outError = [NSError errorWithDomain:NSOSStatusErrorDomain
											code:unimpErr
										userInfo:info];
		}
		return NO;
	}
	[self setEntry:newEntry];
	return YES;	
}

#pragma mark IBActions

- (IBAction)testAction:(id)sender {
	NSLog(@"SPEntryController testAction call");
	NSLog(@"Associated entry:\n%@", entry);
}

- (IBAction)postEntry:(id)sender {
	// Finish editing for any responders
	[[self windowForSheet] endEditingFor:nil];
	
	// Invoke entry posting
	[[SPAccountController sharedController] postEntry:entry];
	
	// Set saved status
	[[self windowControllers] makeObjectsPerformSelector:@selector(setDocumentEdited:) withObject:NO];
}

- (IBAction)toggleInfoDrawer:(id)sender {
	if (drawerOpened) {
		[drawer close];
		drawerOpened = NO;
	} else {
		[drawer openOnEdge:NSMinYEdge];
		drawerOpened = YES;
	}

}

- (IBAction)detectCurrentMusic:(id)sender {
	NSString *currentMusic = [SPAppDelegate getCurrentMusicString];
	
	[currentMusicTextField setStringValue:currentMusic];
	[entry setCurrentMusic:currentMusic];
}

- (IBAction)selectViewMode:(id)sender {
	int selection = [viewModeSegmentedControl selectedSegment];
	
	switch (selection) {
		case 0:
			// Main View selected
			[entryWindow setContentView:previousView];
			[previousView release];
			previousView = nil;
			
			break;
		case 2:
			// Web View selected
			[[self windowForSheet] endEditingFor:nil]; // This should drop focus 
			[self updateModel]; //TODO: Review this with above call
			
			[[previewWebView mainFrame] loadHTMLString:[self HTMLContent] baseURL:nil];
			previousView = [[entryWindow contentView] retain]; //store view here for a while
			[entryWindow setContentView:previewWebView];

			break;
	}
}

- (IBAction)changeJournal:(id)sender {
	LJJournal *journal = [[journalButton selectedItem] representedObject];
	
	if ([journalButton indexOfSelectedItem] > 0) {
		
	}

	[entry setJournal:journal];
}

- (IBAction)changeSecurity:(id)sender {
	int index = [securityButton indexOfSelectedItem];
	
	if(index < 3)
	{
		// If general mode is selected
		[entry setPrivacyLevel:[[[securityButton selectedItem] representedObject] intValue]];
		[[entry allowedGroups] removeAllObjects];
		
		// Set states
		for(NSMenuItem *item in [securityButton itemArray])
		{
			if (item == [securityButton selectedItem])
			{
				[item setState:NSOnState];
			} else {
				[item setState:NSOffState];
			}
		}
		
	} else {
		// If one of groups was selected
		LJFriendsGroup *group = [[securityButton selectedItem] representedObject];
		NSMutableSet *allowedGroups = [entry allowedGroups];
		
		if ([allowedGroups containsObject:group]) {
			[allowedGroups removeObject:group];
			
			if ([allowedGroups count] == 0) {
				LJEntryPrivacyLevel privacyLevel = [[SPPreferencesController sharedController] defaultPrivacyLevel];
				[entry setPrivacyLevel:privacyLevel];
				switch (privacyLevel) {
					case LJPublicEntry:
						[securityButton selectItemAtIndex:0];
						break;
					case LJFriendsOnlyEntry:
						[securityButton selectItemAtIndex:1];
						break;
					case LJPrivateEntry:
						[securityButton selectItemAtIndex:2];
						break;
				}
				return;
			}
			
		} else {
			[allowedGroups addObject:group];
		}
		
		[entry setPrivacyLevel:LJGroupAllowedEntry];
		
		
		// If multiple selected, select "custom"
		if ([allowedGroups count] > 1) {
			[securityButton selectItemAtIndex:3];
		}		
		
		// Set states
		for(int index = 0; index < [[securityButton itemArray] count]; ++index) {
			NSMenuItem *item = [securityButton itemAtIndex:index];
			
			if (index == 3) {
				[item setState:NSOnState];
			} else if (index < 5) {
				[item setState:NSOffState];
			} else if ([allowedGroups containsObject:[item representedObject]]) {
				if ([[entry allowedGroups] count] == 1) {
					[securityButton selectItemAtIndex:index];
				}					
				[item setState:NSMixedState];
			} else {
				[item setState:NSOffState];
			}
		}
	}	
}

- (IBAction)changeSubject:(id)sender {
	[entry setSubject:[subjectTextField stringValue]];
}
- (IBAction)changeContent:(id)sender {
	[entry setContent:[contentTextView string]];
}
- (IBAction)changeUserpic:(id)sender {
	LJUserpic* newUserpic = [[userpicsButton selectedItem] representedObject];
	[entry setUserpic:newUserpic];
	[userpicImageView setImage:[newUserpic image]];
	
}
- (IBAction)changeCurrentMusic:(id)sender {
	[entry setCurrentMusic:[currentMusicTextField stringValue]];
}
- (IBAction)changeCurrentMood:(id)sender {
	[entry setCurrentMood:[currentMoodComboBox stringValue]];
}
- (IBAction)changeLocation:(id)sender {
	[entry setLocation:[locationComboBox stringValue]];	
}
- (IBAction)changeTags:(id)sender {
	// We assume that tagsTokenField contains LJTags 
	[entry setTags:[tagsTokenField objectValue]];
}
- (IBAction)changeBackdatedState:(id)sender {
	if ([backdatedButton state] == NSOnState) {
		[backdatedDatePicker setEnabled:YES];
		[entry setBackdated:[backdatedDatePicker dateValue]];
	} else {
		[backdatedDatePicker setEnabled:NO];
		[entry setBackdated:nil];
	}	
}
- (IBAction)changeBackdatedDate:(id)sender {
	[entry setBackdated:[backdatedDatePicker dateValue]];
}


#pragma mark -
#pragma mark Private

- (void)updateView {
	// Journals menu
	[journalButton selectItemWithTitle:[[entry journal] name]];
	
	// Security menu
	switch ([entry privacyLevel]) {
		case LJDefaultPrivacy:
			[securityButton selectItemAtIndex:0];
			[entry setPrivacyLevel:LJPublicEntry];
			break;
		case LJPublicEntry:
			[securityButton selectItemAtIndex:0];
			break;
		case LJFriendsOnlyEntry:
			[securityButton selectItemAtIndex:1];
			break;
		case LJPrivateEntry:
			[securityButton selectItemAtIndex:2];
			break;
		case LJGroupAllowedEntry:
			if ([[entry allowedGroups] count] != 1) {
				[securityButton selectItemAtIndex:3];
			}
			
			for(int index = 0; index < [[securityButton itemArray] count]; ++index) {
				NSMenuItem *item = [securityButton itemAtIndex:index];
				
				if (index == 3) {
					[item setState:NSOnState];
				} else if (index < 5) {
					[item setState:NSOffState];
				} else if ([[entry allowedGroups] containsObject:[item representedObject]]) {
					if ([[entry allowedGroups] count] == 1) {
						[securityButton selectItemAtIndex:index];
					}					
					[item setState:NSMixedState];
				} else {
					[item setState:NSOffState];
				}
			}
			break;
		default:
			break;
	}
	
	// Userpics menu and image wiew
	[userpicImageView setImage:[[entry userpic] image]];
	[userpicsButton selectItemWithTitle:[[entry userpic] keyword]];
	
	// Subject and content
	[subjectTextField setStringValue:[entry subject]];
	[contentTextView setString:[entry content]];
	
	// Meta-info
	NSMutableString* tagsString = [NSMutableString string];
	for(LJTag* tag in [entry tags]) {
		[tagsString appendFormat:@"%@, ", [tag name]];
	}
	[tagsTokenField setStringValue:tagsString];
	
	[locationComboBox setStringValue:[entry location]];
	[currentMoodComboBox setStringValue:[entry currentMood]];
	[currentMusicTextField setStringValue:[entry currentMusic]];
	
	if ([entry isBackdated]) {
		[backdatedButton setState:NSOnState];
		[backdatedDatePicker setDateValue:[entry backdatedDate]];
	} else {
		[backdatedButton setState:NSOffState];
		[backdatedDatePicker setDateValue:[NSDate date]];
	}

	
}
- (void)updateModel {
	[self changeJournal:nil];
	[self changeTags:nil];
	[self changeContent:nil];
	[self changeSubject:nil];
	[self changeUserpic:nil];
	[self changeLocation:nil];
	[self changeSecurity:nil];
	[self changeCurrentMood:nil];
	[self changeCurrentMusic:nil];
	[self changeBackdatedDate:nil];
	[self changeBackdatedState:nil];
}
		 
- (void)regenerateToolbarMenus {
	LJAccount *account = [[SPAccountController sharedController] sharedAccount];

	if (!account) {
		return;
	}

	// Journals menu
	if ([[account journals] count] == 0) {
		NSLog(@"WARNING! Account with no journals");
	} else {
		NSDictionary *imagesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
										  [NSImage imageNamed:@"userinfo"] , @"user",
										  [NSImage imageNamed:@"communitysmall"] , @"community", nil];
		NSMenu *menu = [account journalsMenuWithImages:imagesDictionary];
		[journalButton setMenu:menu];
		[journalButton selectItemAtIndex:0];
	}
	
	// Security menu
	NSDictionary *imagesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
									  [NSImage imageNamed:@"blankicon"] , @"blank",
									  [NSImage imageNamed:@"private"] , @"private",
									  [NSImage imageNamed:@"protected"] , @"friends",
									  [NSImage imageNamed:@"groupsicon"] , @"custom", nil];
	[securityButton setMenu:[account securityMenuWithImages:imagesDictionary]];
	[securityButton selectItemAtIndex:0];
}
- (void)regenerateUserpicsMenu {
	LJAccount *account = [[SPAccountController sharedController] sharedAccount];
	[userpicsButton setMenu:[account userpicsMenu]];		

	[self changeUserpic:self];
}
		 
- (void)subscribeCallbacks {
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(accountWillReloadCallback)
												 name:LJAccountWillUpdateNotification
											   object:nil];	
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(accountDidReloadCallback)
												 name:LJAccountDidUpdateNotification
											   object:nil];
	
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(accountWillReloadUserpicsCallback)
												 name:LJAccountWillUpdateUserpicsNotification
											   object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(accountDidReloadUserpicsCallback)
												 name:LJAccountDidUpdateUserpicsNotification
											   object:nil];
	
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(entryWillPost)
												 name:LJEntryWillPostNotification
											   object:entry];
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(entryDidPost)
												 name:LJEntryDidPostNotification
											   object:entry];
	
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(changeContent:)
												 name:NSTextDidChangeNotification
											   object:contentTextView];

	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(securityMenuWillOpen)
												 name:NSPopUpButtonWillPopUpNotification
											   object:securityButton];
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(journalsMenuWillOpen)
												 name:NSPopUpButtonWillPopUpNotification
											   object:journalButton];
}

- (NSString *)HTMLContent {
	//TODO: Implement different HTML for comunity post
	
	NSStringEncoding encoding;
	NSError *error;
	NSMutableString *result = [NSMutableString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"EnglishEntry" ofType:@"html"]
														   usedEncoding:&encoding 
																  error:&error];

	NSString *username = [[[entry journal] account] username];
	if (!username) {
		username = @"username";
	}
	[result replaceOccurrencesOfString:@"##username##"
							withString:username
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];

	[result replaceOccurrencesOfString:@"##userpic_url##"
							withString:[[[[userpicsButton selectedItem] representedObject] url] absoluteString] //TODO: point to local cache copy
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];

	NSString *logoPath = [NSString stringWithFormat:@"file://%@", [[NSBundle mainBundle] pathForResource:@"userinfo" ofType:@"gif"]];
	[result replaceOccurrencesOfString:@"##ljman_url##"
							withString:logoPath
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];

	[result replaceOccurrencesOfString:@"##current_datetime##"
							withString:[[NSDate date] descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M:%S" timeZone:nil locale:nil]
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];
	
	[result replaceOccurrencesOfString:@"##current_music##"
							withString:[entry currentMusic]
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];
	[result replaceOccurrencesOfString:@"##current_mood##"
							withString:[entry currentMood]
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];
	[result replaceOccurrencesOfString:@"##current_location##"
							withString:[entry location]
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];

	NSMutableString *tagsString = [NSMutableString string];
	for(LJTag *tag in [entry tags]) {
		[tagsString appendFormat:@"<a href=\"http://%@.livejournal.com/tag/%@\">%@</a> ", username, [tag name], [tag name]];
	}
	[result replaceOccurrencesOfString:@"##tags##"
							withString:tagsString
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];
	
	[result replaceOccurrencesOfString:@"##subject##"
							withString:[entry subject]
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];
	NSMutableString *breakelinedContent = [NSMutableString stringWithString:[entry content]];
	[breakelinedContent replaceOccurrencesOfString:@"\n"
										withString:@"<br/>"
										   options:NSLiteralSearch
											 range:NSMakeRange(0, [breakelinedContent length])];	
	[result replaceOccurrencesOfString:@"##content##"
							withString:breakelinedContent
							   options:NSLiteralSearch
								 range:NSMakeRange(0, [result length])];	
	return result;
}

#pragma mark -
#pragma mark TokenF Fiels Delegate
- (NSArray *)tokenField:(NSTokenField *)tokenField completionsForSubstring:(NSString *)substring indexOfToken:(NSInteger)tokenIndex indexOfSelectedItem:(NSInteger *)selectedIndex
{
	NSMutableArray *substitutions = [NSMutableArray array];
	for (LJTag *tag in [[entry journal] tags])
	{
		if ([[tag name] hasPrefix:substring]) {
			[substitutions addObject:[tag name]];
		}
	}
	return substitutions;	
}

#pragma mark -
#pragma mark Callbacks

- (void)accountWillReloadCallback {
	[journalButton setEnabled:NO];
	[securityButton setEnabled:NO];
}
- (void)accountDidReloadCallback {
	[self regenerateToolbarMenus];
	[contentTextView setAccount:[[entry journal] account]];
	
	[journalButton setEnabled:YES];
	[securityButton setEnabled:YES];
}

- (void)accountWillReloadUserpicsCallback {
	[userpicsButton setEnabled:NO];
}
- (void)accountDidReloadUserpicsCallback {
	[userpicsButton setEnabled:YES];
	LJAccount* account = [[SPAccountController sharedController] sharedAccount];
	[userpicImageView setImage:[[account defaultUserpic] image]];
	[self regenerateUserpicsMenu];
}

- (void)entryWillPost {
    [NSApp beginSheet: progressWindow
       modalForWindow: [self windowForSheet]
        modalDelegate: nil
       didEndSelector: nil
          contextInfo: nil];	
	[progressIndicator startAnimation:self];
}
- (void)entryDidPost {
	[progressIndicator stopAnimation:self];
    [NSApp endSheet:progressWindow];
    [progressWindow orderOut:nil];
}
- (void)entryFailedPost {
	//TODO: Implement error showing
	[progressIndicator stopAnimation:self];
    [NSApp endSheet:progressWindow];
    [progressWindow orderOut:nil];
}


- (void)securityMenuWillOpen {
	// Disable groups selecion when non-default journal is selected
	LJAccount *account = [[SPAccountController sharedController] sharedAccount];
	BOOL itemsEnabled = false;
	if ([entry journal] == [account defaultJournal]) {
		itemsEnabled = true;
	}

	for(int i = 2; i < [[securityButton itemArray] count]; ++i) {
		if (itemsEnabled) {
			[[[securityButton itemArray] objectAtIndex:i] setEnabled:YES];
		} else {
			[[[securityButton itemArray] objectAtIndex:i] setEnabled:NO];
		}
	}
}

- (void)journalsMenuWillOpen {
	// If entry is posted lock account choise
	if ([entry isPosted]) {
		for(int i = 0; i < [[journalButton itemArray] count]; ++i) {
			NSMenuItem *item = [[journalButton itemArray] objectAtIndex:i];
			if ([item isSeparatorItem]) {
				continue;
			}
			if ([item representedObject] == [entry journal]) {
				[item setEnabled:YES];
			} else {
				[item setEnabled:NO];
			}
		}
	} else {
		for(int i = 0; i < [[journalButton itemArray] count]; ++i) {
			[[[journalButton itemArray] objectAtIndex:i] setEnabled:YES];
		}
	}

}

@end
